//
//  GIFAnimatable.swift
//  GifUsage
//
//  Created by Ting Han on 2020/12/19.
//  Copyright © 2020 Ting Han. All rights reserved.
//

import UIKit

protocol GIFAnimatable: class {
    var animator: GIFAnimator? { get set }
    var layer: CALayer { get }
    var frame: CGRect { get set }
    var contentMode: UIView.ContentMode { get set }
}

extension GIFAnimatable {
    func animatorHasNewFrame() {
//        layer.setNeedsDisplay()
        updateImageIfNeeded()
    }
}

public protocol ImageContainer {
    var image: UIImage? { get set }
}

extension GIFAnimatable where Self: ImageContainer {
    var intrinsicContentSize: CGSize {
        return image?.size ?? CGSize.zero
    }
}

extension GIFAnimatable {
    var gifLoopDuration: TimeInterval {
        return animator?.frameStore?.loopDuration ?? 0
    }
    
    var activeFrame: UIImage? {
        return animator?.frameStore?.currentImage
    }
    
    var frameCount: Int {
        return animator?.frameStore?.frameCount ?? 0
    }
    
    var isAnimatingGIF: Bool {
        return animator?.isAnimating ?? false
    }
    
    func animate(withGIFNamed imageName: String, loopCount: Int = 0, preparationBlock: (() -> Void)? = nil, animationBlock: (() -> Void)? = nil) {
        animator?.animate(withGIFNamed: imageName,
                          size: frame.size,
                          contentMode: contentMode,
                          loopCount: loopCount,
                          preparationBlock: preparationBlock,
                          animationBlock: animationBlock)
    }
    
    func animate(withGIFData imageData: Data, loopCount: Int = 0, preparationBlock: (() -> Void)? = nil, animationBlock: (() -> Void)? = nil) {
        animator?.animate(withGIFData: imageData,
                          size: frame.size,
                          contentMode: contentMode,
                          loopCount: loopCount,
                          preparationBlock: preparationBlock,
                          animationBlock: animationBlock)
    }
    
    func animate(withGIFURL imageURL: URL, loopCount: Int = 0, preparationBlock: (() -> Void)? = nil, animationBlock: (() -> Void)? = nil) {
        let session = URLSession.shared
        
        let task = session.dataTask(with: imageURL) { (data, response, error) in
            switch (data, response, error) {
            case (.none, _, let error?):
                print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString)
            case (let data?, _, _):
                DispatchQueue.main.async {
                    self.animate(withGIFData: data, loopCount: loopCount, preparationBlock: preparationBlock, animationBlock: animationBlock)
                }
            default: ()
            }
        }
        
        task.resume()
    }
    
    func prepareForAnimation(withGIFNamed imageName: String,
                             loopCount: Int = 0,
                             completionHandler: (() -> Void)? = nil) {
        animator?.prepareForAnimation(withGIFNamed: imageName,
                                      size: frame.size,
                                      contentMode: contentMode,
                                      loopCount: loopCount,
                                      completionHandler: completionHandler)
    }
    func prepareForAnimation(withGIFData imageData: Data,
                             loopCount: Int = 0,
                             completionHandler: (() -> Void)? = nil) {
        if var imageContainer = self as? ImageContainer {
            imageContainer.image = UIImage(data: imageData)
        }
        
        animator?.prepareForAnimation(withGIFData: imageData,
                                      size: frame.size,
                                      contentMode: contentMode,
                                      loopCount: loopCount,
                                      completionHandler: completionHandler)
    }
    
    func prepareForAnimation(withGIFURL imageURL: URL,
                             loopCount: Int = 0,
                             completionHandler: (() -> Void)? = nil) {
        let session = URLSession.shared
        let task = session.dataTask(with: imageURL) { (data, response, error) in
            switch (data, response, error) {
            case (.none, _, let error?):
                print("Error downloading gif:", error.localizedDescription, "at url:", imageURL.absoluteString)
            case (let data?, _, _):
                DispatchQueue.main.async {
                    self.prepareForAnimation(withGIFData: data,
                                             loopCount: loopCount,
                                             completionHandler: completionHandler)
                }
            default: ()
            }
        }
        
        task.resume()
    }
    
    func prepareForReuse() {
        animator?.prepareForReuse()
    }
    
    func startAnimatingGIF() {
        animator?.startAnimating()
    }
    
     func stopAnimatingGIF() {
        animator?.stopAnimating()
    }
    
     func setShouldResizeFrames(_ resize: Bool) {
        animator?.frameStore?.shouldResizeFrames = resize
    }
    
    func setFrameBufferCount(_ frames: Int) {
        animator?.frameStore?.bufferFrameCount = frames
    }
    
    func updateImageIfNeeded() {
        if var imageContainer = self as? ImageContainer {
            let container = imageContainer
            imageContainer.image = activeFrame ?? container.image
        } else {
            layer.contents = activeFrame?.cgImage
        }
    }
}
